﻿using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;

namespace Neovolve.Extensibility.VisualStudio.TestProfilerLink
{
    /// <summary>
    /// The <see cref="ConfigurationResolver{T}"/>
    /// class is used to resolve the application configurations from the files available.
    /// </summary>
    internal class ConfigurationResolver<T> where T : ApplicationBase
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ConfigurationResolver&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="configurationName">The configuration name.</param>
        internal ConfigurationResolver(String configurationName)
        {
            // Create the collection
            Items = new Collection<T>();

            ResolveConfigurationPath(configurationName);

            Refresh();
        }

        /// <summary>
        /// Finds the item by its name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public T FindByName(String name)
        {
            // Loop through each available framework
            for (Int32 index = 0; index < Items.Count; index++)
            {
                // Check if the framework name matches
                if (Items[index].Name == name)
                {
                    // Return the framework
                    return Items[index];
                }
            }

            // No framework was found
            return null;
        }

        /// <summary>
        /// Refreshes this instance.
        /// </summary>
        public void Refresh()
        {
            RefreshInternal(ConfigurationPath);
        }

        /// <summary>
        /// Refreshes the internal.
        /// </summary>
        /// <param name="path">The path.</param>
        private void RefreshInternal(String path)
        {
            // Clear the collection
            Items.Clear();

            // Exit if the configuration path doesn't exist
            if (Directory.Exists(path) == false)
            {
                return;
            }

            const String ConfigurationExtension = "*.xml";

            String[] configurationFiles = Directory.GetFiles(
                ConfigurationPath, ConfigurationExtension, SearchOption.AllDirectories);
            XmlSerializer serializer = new XmlSerializer(typeof(T));

            // Loop through each file found
            for (Int32 index = 0; index < configurationFiles.Length; index++)
            {
                // Open the file as a stream
                using (
                    FileStream stream = File.Open(
                        configurationFiles[index], FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    // Deserialize the file
                    T item = serializer.Deserialize(stream) as T;

                    // Check if the framework was correctly deserialized
                    if (item != null)
                    {
                        // Ensure that the items application path is expanded
                        item.ApplicationPath = Environment.ExpandEnvironmentVariables(item.ApplicationPath);

                        // Add the framework
                        Items.Add(item);
                    }
                }
            }
        }

        /// <summary>
        /// Resolves the configuration path.
        /// </summary>
        /// <param name="configurationName">Name of the configuration.</param>
        private void ResolveConfigurationPath(String configurationName)
        {
            // Resolve the configuration path
            String assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            ConfigurationPath = Path.Combine(assemblyPath, configurationName);
        }

        /// <summary>
        /// Gets or sets the configuration path.
        /// </summary>
        /// <value>The configuration path.</value>
        public String ConfigurationPath
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the items.
        /// </summary>
        /// <value>The items.</value>
        public Collection<T> Items
        {
            get;
            set;
        }
    }
}